import { FormDemos } from "@/lib/@docs/demos/src";
import { Layout } from "@/layout";
import { MDX_DATA } from "@/mdx";

export default Layout(MDX_DATA.useForm);

## Installation

`@mantine/form` package does not depend on any other libraries, you can use it with or without `@mantine/core` inputs:

<InstallScript packages="@mantine/form" />

## Usage

<Demo data={FormDemos.usage} />

## API overview

All examples below use the following example use-form hook.

```tsx
import { useForm } from "@mantine/form";

const form = useForm({
  mode: "uncontrolled",
  initialValues: {
    path: "",
    path2: "",
    user: {
      firstName: "John",
      lastName: "Doe",
    },
    fruits: [
      { name: "Banana", available: true },
      { name: "Orange", available: false },
    ],
    accepted: false,
  },
});
```

### Values

[Form values guide](/form/values/)

```tsx
// get current form values
form.getValues();

// Set all form values
form.setValues(values);

// Set all form values using the previous state
form.setValues((prev) => ({ ...prev, ...values }));

// Set value of single field
form.setFieldValue("path", value);

// Set value of nested field
form.setFieldValue("user.firstName", "Jane");

// Resets form values to `initialValues`,
// clears all validation errors,
// resets touched and dirty state
form.reset();

// Sets initial values, used when form is reset
form.setInitialValues({ values: "object" });
```

### List items

[Nested fields guide](/form/nested/)

```tsx
// Inserts given list item at the specified path
form.insertListItem("fruits", { name: "Apple", available: true });

// An optional index may be provided to specify the position in a nested field.
// If the index is provided, item will be inserted at the given position.
// If the index is larger than the current list, the element is inserted at the last position.
form.insertListItem("fruits", { name: "Orange", available: true }, 1);

// Removes the list item at the specified path and index.
form.removeListItem("fruits", 1);

// Swaps two items of the list at the specified path.
// You should make sure that there are elements at at the `from` and `to` index.
form.reorderListItem("fruits", { from: 1, to: 0 });
```

### Validation

[Form validation guide](/form/validation/)

```tsx
import { useForm } from "@mantine/form";

const form = useForm({
  mode: "uncontrolled",
  initialValues: {
    email: "",
    user: {
      firstName: "",
      lastName: "",
    },
  },
  validate: {
    email: (value) => (value.length < 2 ? "Invalid email" : null),
    user: {
      firstName: (value) =>
        value.length < 2 ? "First name must have at least 2 letters" : null,
    },
  },
});

// Validates all fields with specified `validate` function or schema, sets form.errors
form.validate();

// Validates single field at specified path, sets form.errors
form.validateField("user.firstName");

// Works the same way as form.validate but does not set form.errors
form.isValid();
form.isValid("user.firstName");
```

### Errors

[Form errors guide](/form/errors/)

Validation errors occur when defined validation rules were violated, `initialErrors` were specified in useForm properties
or validation errors were set manually.

```tsx
// get current errors state
form.errors;

// Set all errors
form.setErrors({ path: "Error message", path2: "Another error" });

// Set error message at specified path
form.setFieldError("user.lastName", "No special characters allowed");

// Clears all errors
form.clearErrors();

// Clears error of field at specified path
form.clearFieldError("path");
```

### onReset and onSubmit

Wrapper function for form `onSubmit` and `onReset` event handler. `onSubmit` handler accepts as second argument a function
that will be called with errors object when validation fails.

```tsx
import { useForm } from "@mantine/form";

function Demo() {
  const form = useForm({ mode: "uncontrolled" });

  const handleSubmit = (values: typeof form.values) => {
    console.log(values);
  };

  return (
    <>
      {/* Supply handle submit as a single argument to receive validated values */}
      <form onSubmit={form.onSubmit(handleSubmit)} />

      {/* Supply second argument to handle errors */}
      <form
        onSubmit={form.onSubmit(
          (values, event) => {
            console.log(
              values, // <- form.getValues() at the moment of submit
              event // <- form element submit event
            );
          },
          (validationErrors, values, event) => {
            console.log(
              validationErrors, // <- form.errors at the moment of submit
              values, // <- form.getValues() at the moment of submit
              event // <- form element submit event
            );
          }
        )}
      />

      {/* form.onReset calls form.reset */}
      <form onReset={form.onReset}></form>
    </>
  );
}
```

### Touched and dirty

[Touched & dirty guide](/form/status/)

```tsx
// Returns true if user interacted with any field inside form in any way
form.isTouched();

// Returns true if user interacted with field at specified path
form.isTouched("path");

// Set all touched values
form.setTouched({ "user.firstName": true, "user.lastName": false });

// Clears touched status of all fields
form.resetTouched();

// Returns true if form values are not deep equal to initialValues
form.isDirty();

// Returns true if field value is not deep equal to initialValues
form.isDirty("path");

// Sets dirty status of all fields
form.setDirty({ "user.firstName": true, "user.lastName": false });

// Clears dirty status of all fields, saves form.values snapshot
// After form.resetDirty is called, form.isDirty will compare
// form.getValues() to snapshot instead of initialValues
form.resetDirty();
```

## UseFormReturnType

`UseFormReturnType` can be used when you want to pass `form` as a prop to another component:

```tsx
import { TextInput } from "@mantine/core";
import { useForm, UseFormReturnType } from "@mantine/form";

interface FormValues {
  name: string;
  occupation: string;
}

function NameInput({ form }: { form: UseFormReturnType<FormValues> }) {
  return <TextInput key={form.key("name")} {...form.getInputProps("name")} />;
}

function OccupationInput({ form }: { form: UseFormReturnType<FormValues> }) {
  return (
    <TextInput
      key={form.key("occupation")}
      {...form.getInputProps("occupation")}
    />
  );
}

function Demo() {
  const form = useForm<FormValues>({
    mode: "uncontrolled",
    initialValues: { name: "", occupation: "" },
  });
  return (
    <>
      <NameInput form={form} />
      <OccupationInput form={form} />
    </>
  );
}
```
